﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Microsoft.AspNetCore.Http;
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using Mapster;
using SqlSugar;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Admin.NET.Application.Entity;
using Admin.NET.Core.Service;
namespace Admin.NET.Application;

/// <summary>
/// 入库申请服务 🧩
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupNameStore, Order = 100)]
public partial class StoreInboundService : IDynamicApiController, ITransient
{
    private readonly SqlSugarRepository<StoreInbound> _storeInboundRep;
    private readonly ISqlSugarClient _sqlSugarClient;
    // 创建全局
    private readonly StoreMaterialsService _storeMaterialsService;
    private readonly StoreProcessFlowStepService _storeProcessFlowStepService;
    private readonly StoreProcessRecordService _storeProcessRecordService;
    private readonly UserManager _userManager;
    private readonly SqlSugarRepository<SysUser> _userRep;

    public StoreInboundService(SqlSugarRepository<StoreInbound> storeInboundRep,
        ISqlSugarClient sqlSugarClient,
        StoreMaterialsService storeMaterialsService,
        StoreProcessFlowStepService storeProcessFlowStepService,
        StoreProcessRecordService storeProcessRecordService
,
        UserManager userManager,
        SqlSugarRepository<SysUser> userRep)
    {
        _storeInboundRep = storeInboundRep;
        _sqlSugarClient = sqlSugarClient;
        _storeMaterialsService = storeMaterialsService;
        _storeProcessFlowStepService = storeProcessFlowStepService;
        _storeProcessRecordService = storeProcessRecordService;
        _userManager = userManager;
        _userRep = userRep;
    }

    /// <summary>
    /// 分页查询入库申请 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("分页查询入库申请")]
    [ApiDescriptionSettings(Name = "Page"), HttpPost]
    public async Task<SqlSugarPagedList<StoreInboundOutput>> Page(PageStoreInboundInput input)
    {
        input.Keyword = input.Keyword?.Trim();
        var query = _storeInboundRep.AsQueryable()
            .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.BatchNo.Contains(input.Keyword) || u.RequestNo.Contains(input.Keyword) || u.StoreTypeName.Contains(input.Keyword) || u.MaterialName.Contains(input.Keyword) || u.ApplicantName.Contains(input.Keyword) || u.ReceiverName.Contains(input.Keyword))
            .WhereIF(!string.IsNullOrWhiteSpace(input.BatchNo), u => u.BatchNo.Contains(input.BatchNo.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.RequestNo), u => u.RequestNo.Contains(input.RequestNo.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.StoreTypeName), u => u.StoreTypeName.Contains(input.StoreTypeName.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.MaterialName), u => u.MaterialName.Contains(input.MaterialName.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.ApplicantName), u => u.ApplicantName.Contains(input.ApplicantName.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.ReceiverName), u => u.ReceiverName.Contains(input.ReceiverName.Trim()))
            .WhereIF(input.RequestTimeRange?.Length == 2, u => u.RequestTime >= input.RequestTimeRange[0] && u.RequestTime <= input.RequestTimeRange[1])
            .WhereIF(input.StatusNewOrChange.HasValue, u => u.StatusNewOrChange == input.StatusNewOrChange)
            .LeftJoin<StoreType>((u, storeType) => u.StoreTypeId == storeType.Id)
            .LeftJoin<StoreMaterials>((u, storeType, material) => u.MaterialId == material.Id)
            .Select((u, storeType, material) => new StoreInboundOutput
            {
                Id = u.Id,
                BatchNo = u.BatchNo,
                RequestNo = u.RequestNo,
                StoreTypeId = u.StoreTypeId,
                StoreTypeDisplayName = $"{storeType.Name}",
                StoreTypeName = u.StoreTypeName,
                MaterialId = u.MaterialId,
                MaterialFkDisplayName = $"{material.Name}",
                MaterialName = u.MaterialName,
                MaterialPrice = u.MaterialPrice,
                Quantity = u.Quantity,
                Unit = u.Unit,
                MaterialPriceTotal = u.MaterialPriceTotal,
                ApplicantId = u.ApplicantId,
                ApplicantName = u.ApplicantName,
                ApplicantDept = u.ApplicantDept,
                ReceiverId = u.ReceiverId,
                ReceiverName = u.ReceiverName,
                ReceiverDept = u.ReceiverDept,
                RequestTime = u.RequestTime,
                ExpectedDate = u.ExpectedDate,
                CompletedTime = u.CompletedTime,
                StatusNewOrChange = (StatusNewOrChangeEnum)u.StatusNewOrChange,
                Remark = u.Remark,
                ConfirmerId = u.ConfirmerId,
                ConfirmerName = u.ConfirmerName,
                TenantId = u.TenantId,
                OrgId = u.OrgId,
                IsDelete = u.IsDelete,
                CreateTime = u.CreateTime,
                UpdateTime = u.UpdateTime,
                CreateUserId = u.CreateUserId,
                CreateUserName = u.CreateUserName,
                UpdateUserId = u.UpdateUserId,
                UpdateUserName = u.UpdateUserName,
            });
        var pagedResult = await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);

        // 构建全分类路径
        // 异步构建每个物资的分类完整路径
        foreach (var item in pagedResult.Items)
        {
            item.StoreTypeDisplayName = await _storeMaterialsService.BuildCategoryPathAsync(item.StoreTypeId);
        }
        return pagedResult;
    }

    /// <summary>
    /// 获取入库申请详情 ℹ️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("获取入库申请详情")]
    [ApiDescriptionSettings(Name = "Detail"), HttpGet]
    public async Task<StoreInbound> Detail([FromQuery] QueryByIdStoreInboundInput input)
    {
        return await _storeInboundRep.GetFirstAsync(u => u.Id == input.Id);
    }

    /// <summary>
    /// 增加入库申请 ➕
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("增加入库申请")]
    [ApiDescriptionSettings(Name = "Add"), HttpPost]
    public async Task<long> Add(AddStoreInboundInput input)
    {
        var entity = input.Adapt<StoreInbound>();
        
        // 判断是否存在ConfirmerId
        // 如果没有，自动根据当前登录账号的ID，获取直属主管Id和直属主管姓名
        // 否则 根据提供的确认人ID，获取确认人姓名
        if (!entity.ConfirmerId.HasValue || entity.ConfirmerId.Value <= 0)
        {
            // 自动获取当前用户的直属主管作为确认人
            var targetUserId = _userManager.UserId;
            var currentUser = await _userRep.AsQueryable()
                .ClearFilter()
                .Where(x => x.Id == targetUserId).FirstAsync();
            if (currentUser?.ManagerUserId != null)
            {
                // 先查询主管基本信息
                var managerUser = await _userRep.AsQueryable()
                    .Where(u => u.Id == currentUser.ManagerUserId.Value && u.Status == StatusEnum.Enable)
                    .FirstAsync();
                if (managerUser != null)
                {
                    entity.ConfirmerId = managerUser.Id;
                    entity.ConfirmerName = managerUser.RealName;
                }
            }
            else
            {
                entity.ConfirmerId = _userManager.UserId;
                entity.ConfirmerName = _userManager.RealName;
            }
        }
        else
        {
            // 根据提供的确认人ID，获取确认人姓名
            // 先查询主管基本信息
            var managerUser = await _userRep.AsQueryable()
                .Where(u => u.Id == entity.ConfirmerId.Value && u.Status == StatusEnum.Enable)
                .FirstAsync();
            if (managerUser != null)
            {
                entity.ConfirmerId = managerUser.Id;
                entity.ConfirmerName = managerUser.RealName;
            }
        }
        
        // 如果没有提供RequestNo，则自动生成
        if (string.IsNullOrWhiteSpace(entity.RequestNo))
        {
            entity.RequestNo = $"IN{DateTime.Now:yyyyMMddHHmmss}";
        }
        
        // 使用事务确保数据一致性
        var result = await _sqlSugarClient.Ado.UseTranAsync(async () =>
        {
            // 插入入库申请
            var success = await _storeInboundRep.InsertAsync(entity);
            if (!success) return 0;
            
            // 更新物资的在途库存
            if (entity.MaterialId.HasValue && entity.Quantity.HasValue)
            {
                await _sqlSugarClient.Updateable<StoreMaterials>()
                    .SetColumns(m => new StoreMaterials 
                    { 
                        InTransitStock = m.InTransitStock + entity.Quantity.Value 
                    })
                    .Where(m => m.Id == entity.MaterialId.Value)
                    .ExecuteCommandAsync();
            }

            // 插入审批流程 - 入库流程：审核 → 完成
            var entityFlow = entity.Adapt<AddStoreProcessFlowStepInput>();
            entityFlow.FlowType = "入库";
            entityFlow.CurrentStep = 1; // 从第一步开始：审核
            entityFlow.TotalSteps = 2;  // 总共2步：审核 → 完成
            // TODO: 设置第一级审批人为物资部门总管
            // entityFlow.ConfirmerId = 物资部门总管ID;
            // entityFlow.ConfirmerName = "物资部门总管";
            await _storeProcessFlowStepService.Add(entityFlow);

            // 创建申请记录 - 记录申请操作
            var processRecord = entity.Adapt<AddStoreProcessRecordInput>();
            processRecord.FlowType = "入库";
            processRecord.ApprovalAction = StatusApprovalEnum.Pending; // 待审核状态
            processRecord.Remark = $"提交入库申请：{entity.MaterialName} x {entity.Quantity} {entity.Unit}";
            processRecord.CompletedTime = DateTime.Now; // 申请提交时间
            // 记录真实的信息
            processRecord.ConfirmerId = _userManager.UserId;
            processRecord.ConfirmerName = _userManager.RealName;

            await _storeProcessRecordService.Add(processRecord);

            return entity.Id;
        });
        
        return result.Data;
    }

    /// <summary>
    /// 更新入库申请 ✏️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("更新入库申请")]
    [ApiDescriptionSettings(Name = "Update"), HttpPost]
    public async Task Update(UpdateStoreInboundInput input)
    {
        var entity = input.Adapt<StoreInbound>();
        await _storeInboundRep.AsUpdateable(entity)
        .IgnoreColumns(u => new {
            u.BatchNo,
            u.RequestNo,
            u.StoreTypeName,
            u.MaterialName,
            u.MaterialPrice,
            u.Unit,
            u.MaterialPriceTotal,
            u.ApplicantName,
            u.ApplicantDept,
            u.ReceiverName,
            u.ReceiverDept,
            u.RequestTime,
            u.CompletedTime,
            u.ConfirmerId,
            u.ConfirmerName,
        })
        .ExecuteCommandAsync();
    }

    /// <summary>
    /// 删除入库申请 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("删除入库申请")]
    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
    public async Task Delete(DeleteStoreInboundInput input)
    {
        var entity = await _storeInboundRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        await _storeInboundRep.FakeDeleteAsync(entity);   //假删除
        //await _storeInboundRep.DeleteAsync(entity);   //真删除
    }

    /// <summary>
    /// 批量删除入库申请 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("批量删除入库申请")]
    [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
    public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<DeleteStoreInboundInput> input)
    {
        var exp = Expressionable.Create<StoreInbound>();
        foreach (var row in input) exp = exp.Or(it => it.Id == row.Id);
        var list = await _storeInboundRep.AsQueryable().Where(exp.ToExpression()).ToListAsync();
   
        return await _storeInboundRep.FakeDeleteAsync(list);   //假删除
        //return await _storeInboundRep.DeleteAsync(list);   //真删除
    }
    
    /// <summary>
    /// 获取下拉列表数据 🔖
    /// </summary>
    /// <returns></returns>
    [DisplayName("获取下拉列表数据")]
    [ApiDescriptionSettings(Name = "DropdownData"), HttpPost]
    public async Task<Dictionary<string, dynamic>> DropdownData(DropdownDataStoreInboundInput input)
    {
        var storeTypeIdData = await _storeInboundRep.Context.Queryable<StoreType>()
            .Select(u => new TreeStoreTypeOutputIn {
                Value = u.Id,
                Label = $"{u.Name}"
            }, true).ToTreeAsync(u => u.Children, u => u.Pid, null);
        var materialIdData = await _storeInboundRep.Context.Queryable<StoreMaterials>()
            .InnerJoinIF<StoreInbound>(input.FromPage, (u, r) => u.Id == r.MaterialId)
            .Select(u => new {
                Value = u.Id,
                Label = $"{u.Name}"
            }).ToListAsync();

        // 用户信息列表
        var userIdData = await _storeInboundRep.Context.Queryable<SysUser>()
            .InnerJoinIF<StoreInbound>(input.FromPage, (u, r) => u.Id == r.ApplicantId)
            .Select(u => new {
                Value = u.Id,
                Label = $"{u.RealName}"
            }).ToListAsync();
        return new Dictionary<string, dynamic>
        {
            { "storeTypeId", storeTypeIdData },
            { "materialId", materialIdData },
            { "userIdData",userIdData },
        };
    }
    
    /// <summary>
    /// 导出入库申请记录 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("导出入库申请记录")]
    [ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify]
    public async Task<IActionResult> Export(PageStoreInboundInput input)
    {
        var list = (await Page(input)).Items?.Adapt<List<ExportStoreInboundOutput>>() ?? new();
        if (input.SelectKeyList?.Count > 0) list = list.Where(x => input.SelectKeyList.Contains(x.Id)).ToList();
        return ExcelHelper.ExportTemplate(list, "入库申请导出记录");
    }
    
    /// <summary>
    /// 下载入库申请数据导入模板 ⬇️
    /// </summary>
    /// <returns></returns>
    [DisplayName("下载入库申请数据导入模板")]
    [ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
    public IActionResult DownloadTemplate()
    {
        return ExcelHelper.ExportTemplate(new List<ExportStoreInboundOutput>(), "入库申请导入模板", (_, info) =>
        {
            if (nameof(ExportStoreMaterialsOutput.StoreTypeDisplayName) == info.Name)
            {
                // 获取所有分类数据并构建完整路径
                var storeTypes = _sqlSugarClient.Queryable<StoreType>().ToList();
                var result = new List<string>();

                foreach (var storeType in storeTypes)
                {
                    var path = _storeMaterialsService.BuildCategoryPath(storeType.Id, storeTypes);
                    if (!string.IsNullOrEmpty(path))
                    {
                        result.Add(path);
                    }
                }
                return result.Distinct().ToList();
            }
            if (nameof(ExportStoreInboundOutput.MaterialFkDisplayName) == info.Name) return _storeInboundRep.Context.Queryable<StoreMaterials>().Select(u => $"{u.Name}").Distinct().ToList();
            return null;
        });
    }
    
    private static readonly object _storeInboundImportLock = new object();
    /// <summary>
    /// 导入入库申请记录 💾
    /// </summary>
    /// <returns></returns>
    [DisplayName("导入入库申请记录")]
    [ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
    public IActionResult ImportData([Required] IFormFile file)
    {
        lock (_storeInboundImportLock)
        {
            var stream = ExcelHelper.ImportData<ImportStoreInboundInput, StoreInbound>(file, (list, markerErrorAction) =>
            {
                _sqlSugarClient.Utilities.PageEach(list, 2048, pageItems =>
                {
                    // 链接 分类 - 支持完整路径匹配
                    var storeTypeIdLabelList = pageItems.Where(x => x.StoreTypeDisplayName != null).Select(x => x.StoreTypeDisplayName).Distinct().ToList();
                    if (storeTypeIdLabelList.Any())
                    {
                        // 获取所有分类数据
                        var allStoreTypes = _sqlSugarClient.Queryable<StoreType>().ToList();
                        var storeTypeIdLinkMap = new Dictionary<string, long?>();

                        // 为每个分类构建完整路径并建立映射
                        foreach (var storeType in allStoreTypes)
                        {
                            var fullPath = _storeMaterialsService.BuildCategoryPath(storeType.Id, allStoreTypes);
                            if (!string.IsNullOrEmpty(fullPath))
                            {
                                storeTypeIdLinkMap[fullPath] = storeType.Id;
                            }
                        }

                        pageItems.ForEach(e => {
                            e.StoreTypeId = storeTypeIdLinkMap.GetValueOrDefault(e.StoreTypeDisplayName ?? "");
                            if (e.StoreTypeId == null) e.Error = "分类链接失败";
                        });
                    }
                    // 链接 物资
                    var materialIdLabelList = pageItems.Where(x => x.MaterialFkDisplayName != null).Select(x => x.MaterialFkDisplayName).Distinct().ToList();
                    if (materialIdLabelList.Any()) {
                        var materialIdLinkMap = _storeInboundRep.Context.Queryable<TreeStoreTypeOutputMa>().Where(u => materialIdLabelList.Contains($"{u.Name}")).ToList().ToDictionary(u => $"{u.Name}", u => u.Id  as long?);
                        pageItems.ForEach(e => {
                            e.MaterialId = materialIdLinkMap.GetValueOrDefault(e.MaterialFkDisplayName ?? "");
                            if (e.MaterialId == null) e.Error = "物资链接失败";
                        });
                    }
                    
                    // 校验并过滤必填基本类型为null的字段
                    var rows = pageItems.Where(x => {
                        if (!string.IsNullOrWhiteSpace(x.Error)) return false;
                        if (x.StatusNewOrChange == null){
                            x.Error = "新增或更新不能为空";
                            return false;
                        }
                        return true;
                    }).Adapt<List<StoreInbound>>();
                    
                    var storageable = _storeInboundRep.Context.Storageable(rows)
                        .SplitError(it => it.Item.Remark?.Length > 128, "备注长度不能超过128个字符")
                        .SplitInsert(_=> true) // 没有设置唯一键代表插入所有数据
                        .ToStorage();
                    
                    storageable.AsInsertable.ExecuteCommand();// 不存在插入
                    storageable.AsUpdateable.UpdateColumns(it => new
                    {
                        it.StoreTypeId,
                        it.MaterialId,
                        it.Quantity,
                        it.ApplicantId,
                        it.ReceiverId,
                        it.ExpectedDate,
                        it.StatusNewOrChange,
                        it.Remark,
                    }).ExecuteCommand();// 存在更新
                    
                    // 标记错误信息
                    markerErrorAction.Invoke(storageable, pageItems, rows);
                });
            });
            
            return stream;
        }
    }
}
